/**
  ******************************************************************************
  * @file    tc32l010_i2c.c
  * @author  CHIPAT Application Team
  * @brief   This file provides firmware functions to manage the following
  *          functionalities of the Inter-Integrated circuit (I2C):
  *           + Initialization and Configuration
  *           + Communications handling
  *           + I2C registers management
  *           + Data transfers management
  *           + DMA transfers management
  *           + Interrupts and flags management
  *
  @verbatim
  ==============================================================================
**/

/* Includes ------------------------------------------------------------------*/
#include "tc32l010_i2c.h"
#include "tc32l010_rcc.h"


/* DATA_CMD register byte 1 (Bits[15:8]) base address */
#define DATA_CMD_BYTE1_ADDRESS          (I2C_BASE + 0x0D)

/* DATA_CMD register byte 0 (Bits[7:0]) base address */
#define DATA_CMD_BYTE0_ADDRESS          (I2C_BASE + 0x0C)

/**
  * @brief  Initializes the I2C according to the specified parameters
  *         in the I2C_InitTypeDef and initialize the associated handle.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
	 * @param  I2C_InitStruct Pointer to a I2C_InitTypeDef structure that contains
	 *                the initialization for the specified I2C.
  * @retval HAL status
  */
void I2C_Init(I2C_TypeDef *I2Cx, I2C_InitTypeDef *I2C_InitStruct)
{
    uint32_t fs_cnt = 0;
    uint32_t cmd = 0;
    uint32_t tmpreg = 0;
    RCC_ClocksTypeDef RCC_ClocksStatus;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_I2C_ACKNOWLEDGE_ADDRESS(I2C_InitStruct->I2C_AcknowledgedAddress));
    assert_param(IS_I2C_CLOCK_SPEED(I2C_InitStruct->I2C_ClockSpeed));
    assert_param(IS_I2C_MODE(I2C_InitStruct->I2C_Mode));
    assert_param(IS_I2C_SET_FILTERTIME(I2C_InitStruct->I2C_FilterTime));

    /* Configure the I2C Speed Rate */
    RCC_GetClocksFreq(&RCC_ClocksStatus);

    if(I2Cx == I2C0)
    {
        fs_cnt = (RCC_ClocksStatus.PCLK_Frequency / I2C_InitStruct->I2C_ClockSpeed) >> 1;
    }
    else
    {
        return ;
    }

    if(I2C_InitStruct->I2C_ClockSpeed == I2C_SPEED_100K)
    {
        cmd |= I2C_CON_SPEED_100K;
    }
    else
    {
        cmd |= I2C_CON_SPEED_400K;
    }

    I2Cx->SCL_CNT = ((fs_cnt - 4) << 16) | fs_cnt;

    I2Cx->ENABLE = 0x00;

    tmpreg = I2Cx->CON;

    tmpreg &= ~(I2C_CON_MASTER_MODE | I2C_CON_SPEED | I2C_CON_10BITADDR);

    I2Cx->CON = tmpreg|I2C_InitStruct->I2C_Mode|cmd|I2C_InitStruct->I2C_AcknowledgedAddress;

    I2Cx->FS_SPKLEN = I2C_InitStruct->I2C_FilterTime;

    I2Cx->INTR_MASK = 0x00;
}

/**
  * @brief  DeInitialize the I2C peripheral.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @retval HAL status
  */
void I2C_DeInit(I2C_TypeDef *I2Cx)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    if (I2Cx == I2C0)
    {
        RCC_APBPeriphResetCmd(RCC_APBPeriph_I2C0, ENABLE);
        RCC_APBPeriphResetCmd(RCC_APBPeriph_I2C0, DISABLE);
    }
}

/**
  * @brief  Enables or disables the specified I2C peripheral.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  NewState: new state of the I2C peripheral.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
        /* Enable the selected I2C peripheral */
        I2Cx->ENABLE |= I2C_ENABLE_ENABLE;
    }
    else
    {
        /* Disable the selected I2C peripheral */
        I2Cx->ENABLE &= ~I2C_ENABLE_ENABLE;
    }
}

/**
  * @brief  Set the I2C SDA Setup time interface.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  SetupTime: This parameter can be any combination of the (0-0xF) values.
  * @param  HoldTime: This parameter can be any combination of the (0-0x1F) values.
  * @retval None
  */
void I2C_SetSDASetup(I2C_TypeDef* I2Cx, uint8_t SetupTime, uint8_t HoldTime)
{
    uint32_t reg;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    reg = I2Cx->SDA_SETUP;

    reg &= ~(I2C_SDA_SETUP_SETUP | I2C_SDA_SETUP_HOLD);

    I2Cx->SDA_SETUP = reg | (HoldTime << 16) |  SetupTime;
}

/**
  * @brief  Set the I2C General call ack interface.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  NewState: new state of the specified i2c interrupts.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_GeneralCallAck(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
        I2Cx->CON |= I2C_CON_ACK_GENNERALCALL;
    }
    else
    {
        I2Cx->CON &= ~I2C_CON_ACK_GENNERALCALL;
    }
}

/**
  * @brief  Enables or disables the specified I2C interrupts.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  I2C_IT: specifies the I2C interrupt sources to be enabled or disabled.
  *          This parameter can be one of the following values:
  *            @arg I2C_IT_RXUNDER: rx buff underflow.
  *            @arg I2C_IT_RXOVER: rx buff overflow.
  *            @arg I2C_IT_RXFULL: rx buff full.
  *            @arg I2C_IT_TXOVER: tx buff overflow.
  *            @arg I2C_IT_TXEMPTY: tx buff empty.
  *            @arg I2C_IT_RDREQ: read request.
  *            @arg I2C_IT_TXABRT: tx abort.
  *            @arg I2C_IT_RXDONE: rx done.
  *            @arg I2C_IT_ACTIVITY: detected the i2c activity.
  *            @arg I2C_IT_STOPDET: detected the stop condition.
  *            @arg I2C_IT_STARTDET: detected the start condition.
  *            @arg I2C_IT_GENCALL: detected the gencall condition.
  * @param  NewState: new state of the specified i2c interrupts.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_ITConfig(I2C_TypeDef* I2Cx, uint16_t I2C_IT, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));
    assert_param(IS_I2C_CONFIG_IT(I2C_IT));

    if (NewState != DISABLE)
    {
        /* Enable the selected I2C interrupts */
        I2Cx->INTR_MASK |= I2C_IT;
    }
    else
    {
        /* Disable the selected I2C interrupts */
        I2Cx->INTR_MASK &= (uint16_t)~I2C_IT;
    }
}

/**
  * @brief  Configures the slave address to be transmitted after start generation.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  Address: specifies the slave address to be programmed.
  * @note   This function should be called before generating start condition.
  * @retval None
  */
void I2C_SlaveAddressConfig(I2C_TypeDef* I2Cx, uint16_t Address)
{
    uint32_t tmpreg = 0;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_I2C_TS_ADDRESS(Address));

    /* Get the old register value */
    tmpreg = I2Cx->SAR;

    /* Reset I2Cx SADD bit [9:0] */
    tmpreg &= ~I2C_SAR_SAR;

    /* Set I2Cx SADD */
    tmpreg |= (uint32_t)((uint32_t)Address & I2C_SAR_SAR);

    /* Store the new register value */
    I2Cx->SAR = tmpreg;
}

/**
  * @brief  Enables or disables the I2C 10-bit addressing mode for the master.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  NewState: new state of the I2C 10-bit addressing mode.
  *          This parameter can be: ENABLE or DISABLE.
  * @note   This function should be called before generating start condition.
  * @retval None
  */
void I2C_10BitAddressingModeCmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
        /* Enable 10-bit addressing mode */
        I2Cx->CON |= I2C_CON_10BITADDR;
    }
    else
    {
        /* Disable 10-bit addressing mode */
        I2Cx->CON &= ~I2C_CON_10BITADDR;
    }
}

/**
  * @brief  Configures the type of transfer request for the master.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  I2C_Direction: specifies the transfer request direction to be programmed.
  *          This parameter can be one of the following values:
  *            @arg I2C_Direction_Transmitter: Master request a write transfer
  *            @arg I2C_Direction_Receiver: Master request a read transfer
  * @retval None
  */
void I2C_MasterRequestConfig(I2C_TypeDef* I2Cx, uint8_t I2C_Direction)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_I2C_DIRECTION(I2C_Direction));

    /* Test on the direction to set/reset the read/write bit */
    if (I2C_Direction == I2C_Direction_Transmitter)
    {
        /* Request a write Transfer */
        *(__IO uint8_t *)DATA_CMD_BYTE1_ADDRESS = 0;
    }
    else
    {
        /* Request a read Transfer */
        *(__IO uint8_t *)DATA_CMD_BYTE1_ADDRESS = I2C_Direction_Receiver;
    }
}

/**
  * @brief  Generates I2Cx communication Restart condition.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  NewState: new state of the I2C STOP condition generation.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_GenerateRestart(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
        /* Generate a STOP condition */
        *(__IO uint8_t *)DATA_CMD_BYTE1_ADDRESS = I2C_RESTART;
    }
    else
    {
        /* Disable the STOP condition generation */
        *(__IO uint8_t *)DATA_CMD_BYTE1_ADDRESS = 0;
    }
}

/**
  * @brief  Generates I2Cx communication STOP condition.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  NewState: new state of the I2C STOP condition generation.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
        /* Generate a STOP condition */
        *(__IO uint8_t *)DATA_CMD_BYTE1_ADDRESS = I2C_STOP;
    }
    else
    {
        /* Disable the STOP condition generation */
        *(__IO uint8_t *)DATA_CMD_BYTE1_ADDRESS = 0;
    }
}

/**
  * @brief  Set the specified I2C slave address.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  Address:  chip slave address.
  * @retval None.
  */
void I2C_SendSlaveAddress(I2C_TypeDef* I2Cx, uint16_t Address)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    /* Send the address */
    I2Cx->TAR &= ~I2C_TAR_TAR;

    I2Cx->TAR |= (Address & I2C_TAR_TAR);
}

/**
  * @brief  Set the specified I2C slave address.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  Select:  select the mode.
  * @retval None.
  */
void I2C_SelectBoardOrStartByte(I2C_TypeDef* I2Cx, uint8_t Select)
{
    uint32_t reg;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    reg = I2Cx->TAR;

    reg &= ~(I2C_TAR_GC_OR_START | I2C_TAR_SPECIAL);

    if(Select > I2C_TS_NONE)
    {
        reg |= I2C_TAR_SPECIAL;

        if(Select == I2C_TS_STARTBYTE)
        {
            reg |= I2C_TAR_GC_OR_START;
        }
    }

    I2Cx->TAR = reg;
}

/**
  * @brief  Sends a data byte through the I2Cx peripheral.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  Data:  8-bit data.
  * @retval None.
  */
void I2C_SendData(I2C_TypeDef* I2Cx, uint16_t Data)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    /* Write in the DR register the data to be sent */
    I2Cx->DATA_CMD = Data;
}

/**
  * @brief  received data by the I2Cx peripheral.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @retval Data:  8-bit data.
  */
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    /* Return the data in the DR register */
    return (*(__IO uint8_t *)DATA_CMD_BYTE0_ADDRESS);
}

/**
  * @brief  Enables or disables the I2C DMA interface.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  I2C_DMAReq: specifies the I2C DMA transfer request to be enabled or disabled.
  *          This parameter can be any combination of the following values:
  *            @arg I2C_DMAReq_Tx: Tx DMA transfer request
  *            @arg I2C_DMAReq_Rx: Rx DMA transfer request
  * @param  NewState: new state of the selected I2C DMA transfer request.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_DMACmd(I2C_TypeDef* I2Cx, uint32_t I2C_DMAReq, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));
    assert_param(IS_I2C_DMA_REQ(I2C_DMAReq));

    if (NewState != DISABLE)
    {
        /* Enable the selected I2C DMA requests */
        I2Cx->DMA_CR |= I2C_DMAReq;
    }
    else
    {
        /* Disable the selected I2C DMA requests */
        I2Cx->DMA_CR &= (uint32_t)~I2C_DMAReq;
    }
}

/**
  * @brief  Set the I2C DMA FIFO interface.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  Tx_FIFO: This parameter can be any combination of the (0-1) values.
  * @param  Rx_FIFO: This parameter can be any combination of the (0-1) values.
  * @retval None
  */
void I2C_SetDMAFIFO(I2C_TypeDef* I2Cx, uint32_t Tx_FIFO, uint32_t Rx_FIFO)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_I2C_DMA_FIFOSIZE(Tx_FIFO));
    assert_param(IS_I2C_DMA_FIFOSIZE(Rx_FIFO));

    I2Cx->DMA_TDLR = Tx_FIFO;

    I2Cx->DMA_RDLR = Rx_FIFO;
}

/**
  * @brief  Get the specified I2C status.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @retval The new state of I2C_STATUS .
  *         This parameter can be one of the following values:
  * @param I2C_STATUS_ACTIVITY:Idle or not.
  * @param I2C_STATUS_TFNF:Tx fifo full or not.
  * @param I2C_STATUS_TFE:Tx fifo empty or not.
  * @param I2C_STATUS_RFNE:Rx fifo empty or not.
  * @param I2C_STATUS_RFF:Rx fifo full or not.
  */
uint32_t I2C_GetFlagStatus(I2C_TypeDef* I2Cx)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    /* Return the I2C_FLAG status */
    return  I2Cx->STATUS;
}

/**
  * @brief  Checks whether the specified I2C interrupt flag is set or not.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @retval The new state of i2c_intr_stat.
  *          This parameter can be one of the following values:
  *            @arg I2C_IT_RXUNDER: rx buff underflow.
  *            @arg I2C_IT_RXOVER: rx buff overflow.
  *            @arg I2C_IT_RXFULL: rx buff full.
  *            @arg I2C_IT_TXOVER: tx buff overflow.
  *            @arg I2C_IT_TXEMPTY: tx buff empty.
  *            @arg I2C_IT_RDREQ: read request.
  *            @arg I2C_IT_TXABRT: tx abort.
  *            @arg I2C_IT_RXDONE: rx done.
  *            @arg I2C_IT_ACTIVITY: detected the i2c activity.
  *            @arg I2C_IT_STOPDET: detected the stop condition.
  *            @arg I2C_IT_STARTDET: detected the start condition.
  *            @arg I2C_IT_GENCALL: detected the gencall condition.
  */
uint16_t I2C_GetITStatus(I2C_TypeDef* I2Cx)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    return I2Cx->INTR_STAT;
}

/**
  * @brief  Clear the specified I2C interrupt flag.
  * @param  I2Cx: where x can be 0 to select the I2C peripheral.
  * @param  I2C_IT: specifies the I2C interrupt sources to be enabled or disabled.
  *          This parameter can be one of the following values:
  *            @arg I2C_IT_RXUNDER: rx buff underflow.
  *            @arg I2C_IT_RXOVER: rx buff overflow.
  *            @arg I2C_IT_TXOVER: tx buff overflow.
  *            @arg I2C_IT_RDREQ: read request.
  *            @arg I2C_IT_TXABRT: tx abort.
  *            @arg I2C_IT_RXDONE: rx done.
  *            @arg I2C_IT_ACTIVITY: detected the i2c activity.
  *            @arg I2C_IT_STOPDET: detected the stop condition.
  *            @arg I2C_IT_STARTDET: detected the start condition.
  *            @arg I2C_IT_GENCALL: detected the gencall condition.
  *            @arg I2C_IT_CLEARALL: clear all intrrupt flag
  * @retval None.
  */
void I2C_ClearITPending(I2C_TypeDef* I2Cx, uint32_t I2C_IT)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_I2C_CLEAR_IT(I2C_IT));

    I2Cx->INTR_CLR = I2C_IT;
}

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT CHIPAT *****END OF FILE****/
